#include "common.h"
#include "memory.h"
#include <string.h>
#include <elf.h>

#define ELF_OFFSET_IN_DISK 0

#ifdef HAS_DEVICE
void ide_read(uint8_t *, uint32_t, uint32_t);
#else
void ramdisk_read(uint8_t *, uint32_t, uint32_t);
#endif

#define STACK_SIZE (1 << 20) 

void create_video_mapping();
uint32_t get_ucr3();

uint32_t loader()//实现一个简单的 ELF 加载器，将一个 ELF 格式的可执行文件加载到内存中，并返回程序的入口地址，准备进行程序的实际执行.
{
	Elf32_Ehdr *elf;
	Elf32_Phdr *ph = NULL, *eph;

	uint8_t buf[4096]; // 用于存储从磁盘或内存中读取的 ELF 文件头

#ifdef HAS_DEVICE
	ide_read(buf, ELF_OFFSET_IN_DISK, 4096); // 从磁盘读取 ELF 文件头
#else
	ramdisk_read(buf, ELF_OFFSET_IN_DISK, 4096); // 从内存读取 ELF 文件头
#endif

	elf = (void *)buf; // 将 buf 转换为 ELF 文件头的指针

	/* TODO: fix the magic number with the correct one */
	const uint32_t elf_magic = 0x464c457f; // ELF 文件的魔数(这是固定的，任何ELF文件都一样，和其他文件作区分)
	uint32_t *p_magic = (void *)buf;
	nemu_assert(*p_magic == elf_magic); // 确认文件是一个合法的 ELF 文件

	/* Load each program segment */
	// panic("please implement me");
	for (ph = (void *)buf + elf->e_phoff, eph = ph + elf->e_phnum; ph < eph; ph++)
	{
		/* Scan the program header table, load each segment into memory */
		// 扫描程序头表，加载每个段到内存
		if (ph->p_type == PT_LOAD) // 检查段类型是否为可加载段
		{

			uint32_t pa = ph->p_vaddr; // 获取段的虚拟地址

			/* TODO: read the content of the segment from the ELF file
			 * to the memory region [VirtAddr, VirtAddr + FileSiz)
			 */
			// 将段的内容从 ELF 文件中读取到内存中的 [VirtAddr, VirtAddr + FileSiz) 区域
			ramdisk_read((void *)pa, ELF_OFFSET_IN_DISK + ph->p_offset, ph->p_filesz);

			/* TODO: zero the memory region
			 * [VirtAddr + FileSiz, VirtAddr + MemSiz)
			 */
			// 将 [VirtAddr + FileSiz, VirtAddr + MemSiz) 区域清零
			memset((void *)pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);

#ifdef IA32_PAGE
			/* Record the program break for future use. */
			// 记录程序的 break 值，用于后续的内存管理
			extern uint32_t cur_brk, max_brk;
			uint32_t new_brk = ph->p_vaddr + ph->p_memsz - 1;
			if (cur_brk < new_brk)
			{
				max_brk = cur_brk = new_brk; // 更新 break 值
			}
#endif
		}
	}

	volatile uint32_t entry = elf->e_entry; // 获取程序的入口地址

#ifdef IA32_PAGE
	mm_malloc(KOFFSET - STACK_SIZE, STACK_SIZE); // 为内核堆栈分配内存

#ifdef HAS_DEVICE
	create_video_mapping(); // 创建视频内存的映射（如果有设备）
#endif

	write_cr3(get_ucr3()); // 设置页目录寄存器 CR3
#endif

	return entry; // 返回程序的入口地址
}
